import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.metrics import (
    accuracy_score,
    normalized_mutual_info_score,
    adjusted_rand_score,
    f1_score
)
from sklearn.preprocessing import LabelEncoder


class KMeans:
    """K均值聚类算法实现
    
    参数:
        n_clusters (int): 聚类数量
        max_iter (int): 最大迭代次数
        tol (float): 收敛阈值
        random_state (int): 随机种子
    """
    def __init__(self, n_clusters=3, max_iter=300, tol=1e-4, random_state=None):
        self.n_clusters = n_clusters
        self.max_iter = max_iter
        self.tol = tol
        self.random_state = random_state
        self.centroids = None  # 存储最终质心坐标
        self.labels_ = None    # 存储样本所属簇标签
        self.inertia_ = None   # 存储样本到最近质心的距离总和

    def fit(self, X_data):
        """训练模型并聚类数据
        
        参数:
            X_data (np.ndarray): 输入数据
        """
        if self.random_state is not None:
            np.random.seed(self.random_state)
            
        # 初始化质心：从数据中随机选择n_clusters个样本作为初始质心
        n_samples = X_data.shape[0]
        random_indices = np.random.choice(n_samples, self.n_clusters, replace=False)
        self.centroids = X_data[random_indices]
        
        for _ in range(self.max_iter):
            # 计算所有样本到各质心的欧氏距离平方
            distances = self._calc_distances(X_data)
            # 将每个样本分配到最近的质心
            labels = np.argmin(distances, axis=1)
            
            # 计算新的质心：每个簇的均值
            new_centroids = np.array([X_data[labels == k].mean(axis=0) 
                                    for k in range(self.n_clusters)])
            
            # 检查收敛条件：新旧质心的变化是否小于阈值
            if np.allclose(self.centroids, new_centroids, atol=self.tol):
                break
                
            self.centroids = new_centroids
        
        # 保存最终结果
        self.labels_ = labels
        self.inertia_ = np.sum(np.min(distances, axis=1))
        
    def _calc_distances(self, X_data):
        """计算样本到各质心的欧氏距离平方
        
        参数:
            X_data (np.ndarray): 输入数据
        返回:
            np.ndarray: 距离矩阵
        """
        return np.array([np.sum((X_data - centroid)**2, axis=1) 
                        for centroid in self.centroids]).T

    def predict(self, X_data):
        """预测新样本的簇标签
        
        参数:
            X_data (np.ndarray): 输入数据
        返回:
            np.ndarray: 样本所属簇标签
        """
        distances = self._calc_distances(X_data)
        return np.argmin(distances, axis=1)


def load_iris_data(file_path):
    """加载并预处理鸢尾花数据集
    
    参数:
        file_path (str): 数据文件路径
    返回:
        tuple: 特征矩阵和标签数组
    """
    data = pd.read_csv(file_path)
    features = data.iloc[:, :-1].values  # 所有行，除最后一列外的所有列
    labels = data.iloc[:, -1].values     # 所有行，最后一列
    
    # 将文本标签编码为数值
    label_encoder = LabelEncoder()
    labels = label_encoder.fit_transform(labels)
    
    return features, labels


def evaluate_clustering(y_true, y_pred):
    """评估聚类效果
    
    参数:
        y_true (np.ndarray): 真实标签
        y_pred (np.ndarray): 预测标签
    返回:
        dict: 包含多个评估指标的字典
    """
    return {
        'Accuracy': accuracy_score(y_true, y_pred),
        'NMI': normalized_mutual_info_score(y_true, y_pred),
        'ARI': adjusted_rand_score(y_true, y_pred),
        'F_measure': f1_score(y_true, y_pred, average='weighted')
    }


def visualize_results(X_data, y_true, y_pred, feature_names=None):
    """可视化聚类结果
    
    参数:
        X_data (np.ndarray): 输入数据
        y_true (np.ndarray): 真实标签
        y_pred (np.ndarray): 预测标签
        feature_names (list): 特征名称列表
    """
    plt.figure(figsize=(15, 6))
    
    # 真实标签可视化
    plt.subplot(1, 2, 1)
    plt.scatter(X_data[:, 0], X_data[:, 1], c=y_true, cmap='viridis')
    plt.title('True Clusters')
    if feature_names:
        plt.xlabel(feature_names[0])
        plt.ylabel(feature_names[1])
    
    # 预测标签可视化
    plt.subplot(1, 2, 2)
    plt.scatter(X_data[:, 0], X_data[:, 1], c=y_pred, cmap='viridis')
    plt.title('Predicted Clusters')
    if feature_names:
        plt.xlabel(feature_names[0])
        plt.ylabel(feature_names[1])
    
    plt.tight_layout()
    plt.show()


if __name__ == "__main__":
    # 加载鸢尾花数据集
    features, labels = load_iris_data(r'C:\Users\31351\Desktop\20221202663-exp1.赖\实验6\src\iris.csv')
    
    # 创建并训练KMeans模型
    kmeans = KMeans(n_clusters=3, max_iter=300, random_state=42)
    kmeans.fit(features)
    predicted_labels = kmeans.labels_
    
    # 评估聚类效果
    metrics = evaluate_clustering(labels, predicted_labels)
    print("聚类评估指标:")
    for name, value in metrics.items():
        print(f"{name}: {value:.4f}")
    
    # 可视化聚类结果
    visualize_results(features, labels, predicted_labels, feature_names=['Sepal Length', 'Sepal Width'])